module MODULE_wallace_tree#(
  parameter WIDTH=132
) (
	input	 						 clk_i,
	input							 rst_i,
	input							 valid_i,
  input  [WIDTH-1:0] in [32:0],
  output [WIDTH-1:0] result_o,
	output						 result_valid_o
);

  wire  [WIDTH-1:0] s_row1[11:0];
  wire  [WIDTH-1:0] c_row1[11:0];
  wire  [WIDTH-1:0] c_row1_shift[11:0];
  wire  [WIDTH-1:0] s_row1_r[11:0];
  wire  [WIDTH-1:0] c_row1_shift_r[11:0];
	wire							valid1_r;

  wire  [WIDTH-1:0] s_row2[6:0];
  wire  [WIDTH-1:0] c_row2[6:0];
  wire  [WIDTH-1:0] c_row2_shift[6:0];
  
  wire  [WIDTH-1:0] s_row3[4:0];
  wire  [WIDTH-1:0] c_row3[4:0];
  wire  [WIDTH-1:0] c_row3_shift[4:0];
  wire  [WIDTH-1:0] s_row3_r[4:0];
  wire  [WIDTH-1:0] c_row3_shift_r[4:0];
	wire							valid3_r;

  wire  [WIDTH-1:0] s_row4[2:0];
  wire  [WIDTH-1:0] c_row4[2:0];
  wire  [WIDTH-1:0] c_row4_shift[2:0];

  wire  [WIDTH-1:0] s_row5[1:0];
  wire  [WIDTH-1:0] c_row5[1:0];
  wire  [WIDTH-1:0] c_row5_shift[1:0];
  wire  [WIDTH-1:0] s_row5_r[1:0];
  wire  [WIDTH-1:0] c_row5_shift_r[1:0];
  wire  [WIDTH-1:0] c_row4_shift_r;
	wire							valid5_r;

  wire  [WIDTH-1:0] s_row6, c_row6, c_row6_shift;
  wire  [WIDTH-1:0] s_row7_r,c_row6_shift_r,c_row7_shift_r;
  wire  [WIDTH-1:0] s_row7, c_row7, c_row7_shift;
  wire  [WIDTH-1:0] s_row8, c_row8, c_row8_shift;
	wire valid7_r;
  wire [WIDTH-1:0] out;

  // 1. first level, 11 csa, 33p -> 22p:
genvar i;
generate
	for(i=0; i<11; i=i+1)begin
MODULE_csa_n#(.N(WIDTH)) csa_row1  (in[3*i] , in[3*i+1] , in[3*i+2] , s_row1[i], c_row1[i]);
  	assign c_row1_shift[i] = {c_row1[i][WIDTH-2:0], 1'b0};
	end
endgenerate
// pipline reg1
generate
	for(i=0;i<12; i++) begin
Reg #(132,0) pipline_s_row1(clk_i,rst_i,s_row1[i],s_row1_r[i],valid_i);
Reg #(132,0) pipline_c_row1_shift(clk_i,rst_i,c_row1_shift[i],c_row1_shift_r[i],valid_i);
	end
endgenerate
Reg#(1,0) pipline_valid1(clk_i,rst_i,valid_i,valid1_r,1);

// 2. second level, 7 csa, 22p -> 15p (2*7+1):
MODULE_csa_n #(.N(WIDTH)) csa_row2_0  (s_row1_r[0]      , c_row1_shift_r[0],  s_row1_r[1]      ,  s_row2[0], c_row2[0]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_1  (c_row1_shift_r[1], s_row1_r[2]      ,  c_row1_shift_r[2],  s_row2[1], c_row2[1]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_2  (s_row1_r[3]      , c_row1_shift_r[3],  s_row1_r[4]      ,  s_row2[2], c_row2[2]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_3  (c_row1_shift_r[4], s_row1_r[5]      ,  c_row1_shift_r[5],  s_row2[3], c_row2[3]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_4  (s_row1_r[6]      , c_row1_shift_r[6],  s_row1_r[7]      ,  s_row2[4], c_row2[4]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_5  (c_row1_shift_r[7], s_row1_r[8]      ,  c_row1_shift_r[8],  s_row2[5], c_row2[5]);
MODULE_csa_n #(.N(WIDTH)) csa_row2_6  (s_row1_r[9]      , c_row1_shift_r[9],  s_row1_r[10]     ,  s_row2[6], c_row2[6]);
generate
	for(i=0; i<7; i=i+1)begin
  	assign c_row2_shift[i] = {c_row2[i][WIDTH-2:0], 1'b0};
	end
endgenerate
// 3. third level, 5 csa, 15p -> 10p:
MODULE_csa_n #(.N(WIDTH)) csa_row3_0  (c_row1_shift_r[10], s_row2[0]      , c_row2_shift[0], s_row3[0], c_row3[0]);
MODULE_csa_n #(.N(WIDTH)) csa_row3_1  (s_row2[1]       , c_row2_shift[1], s_row2[2]      , s_row3[1], c_row3[1]);
MODULE_csa_n #(.N(WIDTH)) csa_row3_2  (c_row2_shift[2] , s_row2[3]      , c_row2_shift[3], s_row3[2], c_row3[2]);
MODULE_csa_n #(.N(WIDTH)) csa_row3_3  (s_row2[4]       , c_row2_shift[4], s_row2[5]      , s_row3[3], c_row3[3]);
MODULE_csa_n #(.N(WIDTH)) csa_row3_4  (c_row2_shift[5] , s_row2[6]      , c_row2_shift[6], s_row3[4], c_row3[4]);
generate
	for(i=0; i<5; i=i+1)begin
  	assign c_row3_shift[i] = {c_row3[i][WIDTH-2:0], 1'b0};
	end
endgenerate
// pipline reg2
generate
	for(i=0;i<5; i++) begin
Reg #(132,0) pipline_s_row3(clk_i,rst_i,s_row3[i],s_row3_r[i],valid1_r);
Reg #(132,0) pipline_c_row3_shift(clk_i,rst_i,c_row3_shift[i],c_row3_shift_r[i],valid1_r);
	end
endgenerate
Reg#(1,0) pipline_valid3(clk_i,rst_i,valid1_r,valid3_r,1);

// 4. fourth level, 3 csa, 10p -> 7p (2*3+1):
MODULE_csa_n #(.N(WIDTH)) csa_row4_0  (s_row3_r[0]       , c_row3_shift_r[0], s_row3_r[1]      , s_row4[0], c_row4[0]);
MODULE_csa_n #(.N(WIDTH)) csa_row4_1  (c_row3_shift_r[1] , s_row3_r[2]      , c_row3_shift_r[2], s_row4[1], c_row4[1]);
MODULE_csa_n #(.N(WIDTH)) csa_row4_2  (s_row3_r[3]       , c_row3_shift_r[3], s_row3_r[4]      , s_row4[2], c_row4[2]);
generate
	for(i=0; i<3; i=i+1)begin
  	assign c_row4_shift[i] = {c_row4[i][WIDTH-2:0], 1'b0};
	end
endgenerate
// 5. fifth level, 2 csa, 7p -> 5p (2*2+1):
MODULE_csa_n #(.N(WIDTH)) csa_row5_0  (c_row3_shift_r[4], s_row4[0]      , c_row4_shift[0], s_row5[0], c_row5[0]);
MODULE_csa_n #(.N(WIDTH)) csa_row5_1  (s_row4[1]      , c_row4_shift[1], s_row4[2]      , s_row5[1], c_row5[1]);
generate
  for(i=0; i<2; i=i+1)begin: csa_row5_shift
  	assign c_row5_shift[i] = {c_row5[i][WIDTH-2:0], 1'b0};
	end
endgenerate

// pipline reg3
generate
	for(i=0;i<2; i++) begin
Reg #(132,0) pipline_s_row5(clk_i,rst_i,s_row5[i],s_row5_r[i],valid3_r);
Reg #(132,0) pipline_c_row5_shift(clk_i,rst_i,c_row5_shift[i],c_row5_shift_r[i],valid3_r);
	end
endgenerate
Reg#(1,0) pipline_valid5(clk_i,rst_i,valid3_r,valid5_r,1);
Reg #(132,0) pipline_c_row4_shift(clk_i,rst_i,c_row4_shift[2],c_row4_shift_r,valid3_r);

// 6. sixth level, 1 csa, 5p -> 4p (2+2): 
MODULE_csa_n #(.N(WIDTH)) csa_row6    (c_row4_shift_r, s_row5_r[0], c_row5_shift_r[0], s_row6, c_row6);
assign c_row6_shift = {c_row6[WIDTH-2:0], 1'b0};

// 7. seventh level, 1 csa, 4p -> 3p (2+1):
MODULE_csa_n #(.N(WIDTH)) csa_row7    (s_row5_r[1], c_row5_shift_r[1], s_row6, s_row7, c_row7);
assign c_row7_shift = {c_row7[WIDTH-2:0], 1'b0};

// pipline reg4
Reg#(1,0) pipline_valid7(clk_i,rst_i,valid5_r,valid7_r,1);
Reg #(132,0) pipline_s_row7(clk_i,rst_i,s_row7,s_row7_r,valid5_r);
Reg #(132,0) pipline_c_row7_shift(clk_i,rst_i,c_row7_shift,c_row7_shift_r,valid5_r);
Reg #(132,0) pipline_c_row6_shift(clk_i,rst_i,c_row6_shift,c_row6_shift_r,valid5_r);

// 8. eighth level, 1 csa, 3p -> 2p: 
MODULE_csa_n #(.N(WIDTH)) csa_row8    (c_row6_shift_r, s_row7_r, c_row7_shift_r, s_row8, c_row8);
assign c_row8_shift = {c_row8[WIDTH-2:0], 1'b0};

// 9. output 2p :
assign out=c_row8_shift + s_row8; 

// pipline reg5
Reg #(132,0) pipline_result(clk_i,rst_i,out,result_o,valid7_r);
Reg#(1,0) pipline_valid_o(clk_i,rst_i,valid7_r,result_valid_o,1);


endmodule
